import { number } from 'echarts'

/**
 * 判读字符是否是运算符
 * @param {string} char 字符
 * @returns {Boolean}
 */
export function isOperator (char) {
  const operatorString = '+-*/()'
  return operatorString.indexOf(char) > -1
}

/**
 * 获取优先级
 * 加减为1，乘除为2，其他为0。
*/
function getPrioraty (value) {
  switch (value) {
    case '+':
    case '-':
      return 1
    case '*':
    case '/':
      return 2
    default:
      return 0
  }
}

/**
 * 比较运算符优先级
*/
function prioraty (o1, o2) {
  return getPrioraty(o1) >= getPrioraty(o2)
}

/**
 * @constant {Object} STATUS 状态
 * STATUS.BEGINNING 起始状态
 * STATUS.OPERATOR 运算符
 * STATUS.FACTOR 公式因子
 * STATUS.VALUE 值 只在计算时使用
*/
export const STATUS = {
  BEGINNING: 'BEGINNING',
  OPERATOR: 'OPERATOR',
  FACTOR: 'FACTOR',
  VALUE: 'VALUE'
}

/**
 * 解析公式表达式
 * @param {string} formulaStr 公式表达式
 * @returns {Array} result 解析结果
 */
export function parseFormulaStr (formulaStr) {
  // 过滤掉空白字符
  formulaStr = formulaStr.replace(/\s/g, '')

  const result = []
  let state = STATUS.BEGINNING
  let index = 0
  let subString = ''
  let char = ''

  // 推出当前状态
  function exit () {
    result.push({
      state: state,
      subString: subString
    })
    state = STATUS.BEGINNING
    subString = ''
  }

  // 进入新的状态
  function newState (newState) {
    subString += char
    state = newState
    index++
  }

  while (index < formulaStr.length) {
    char = formulaStr[index]

    switch (state) {
      case STATUS.BEGINNING:
        if (isOperator(char)) {
          newState(STATUS.OPERATOR)
        } else {
          newState(STATUS.FACTOR)
        }
        break
      case STATUS.OPERATOR:
        exit()
        break
      case STATUS.FACTOR:
        if (isOperator(char)) {
          exit()
        } else {
          newState(STATUS.FACTOR)
        }
        break
      default:
        break
    }
  }

  if (state !== STATUS.BEGINNING) {
    exit()
  }

  return result
}

/**
 * 生成逆波兰式
 * @param {Array} parseResult 公式表达式的解析结果 来自 parseFormulaStr
 * @returns {Array} outputStack 逆波兰式（后缀表达式）
 */
export function dal2Rpn (parseResult) {
  /**
  * 代码流程
  * 1 首先创建两个栈 用Array模拟
  * operatorStack 用于存储运算符的栈 越靠近栈顶优先级越高
  * outputStack 用于存储输出结果的栈
  *
  * 2 从中缀式的左端开始逐个读取待处理项cur，逐序进行如下步骤：
  * 2.1 若cur是公式中的因子(FACTOR)，将cur直接压入栈outputStack；
  * 2.2 若cur是运算符(OPERATOR)，则分情况讨论：
  * 2.2.1 若cur是'('，则直接压入栈operatorStack；
  * 2.2.2 若cur是')'则将距离栈operatorStack栈顶的最近的'('之间的运算符，逐个出栈，依次压入栈outputStack，此时抛弃'('；
  * 2.2.3 若cur是除'('和')'外的运算符：
  *   如果检查operatorStack的栈顶的运算符的优先级。
  *   如果栈顶的运算符的优先级大于等于cur的优先级，将operatorStack的栈顶元素压入到outputStack中，
  *   直到栈operatorStack的栈顶运算符优先级别低于cur的优先级。
  *   将cur压入operatorStack
  *   注：'('的优先级为 0，operatorStack的栈顶的运算符为'('时，cur直接压入operatorStack中
  *
  * 3 检查栈operatorStack是否为空，若不为空，则将栈中元素依次弹出并压入栈outputStack中
  *
  * 参考：https://www.cnblogs.com/stay-foolish/archive/2012/04/25/2470590.html
  *      https://www.jb51.net/article/53537.htm
  *      https://www.cnblogs.com/weixinyuai/p/4280036.html
  * TODO：按《数据结构与算法分析》修正逻辑。
  */
  const inputArray = parseResult.map(d => d) // 避免直接操作parseResult
  // 操作符栈
  const operatorStack = []
  // 输出
  const outputStack = []
  // 当前的待处理项
  let cur

  while (inputArray.length > 0) {
    cur = inputArray.shift()

    if (isOperator(cur.subString)) {
      // 代码流程 2.2
      if (cur.subString === '(') {
        // 代码流程 2.2.1
        operatorStack.push(cur)
      } else if (cur.subString === ')') {
        // 代码流程 2.2.2
        var po = operatorStack.pop()
        while (po.subString !== '(' && operatorStack.length > 0) {
          outputStack.push(po)
          po = operatorStack.pop()
        }

        if (po.subString !== '(') {
          throw new Error('error: unmatched ()')
        }
      } else {
        // 代码流程 2.2.3
        while (
          operatorStack.length > 0 &&
          prioraty(operatorStack[operatorStack.length - 1].subString, cur.subString)
        ) {
          outputStack.push(operatorStack.pop())
        }
        operatorStack.push(cur)
      }
    } else {
      // 代码流程 2.1
      outputStack.push(cur)
    }
  }

  if (operatorStack.length > 0) {
    if (
      operatorStack[operatorStack.length - 1].subString === ')' ||
      operatorStack[operatorStack.length - 1].subString === '('
    ) {
      throw new Error('error: unmatched ()')
    }
    while (operatorStack.length > 0) {
      outputStack.push(operatorStack.pop())
    }
  }

  return outputStack
}
let nowmap
let nowItem
// 逆波兰式的计算 getResult为负责处理 两个值的计算 过程
export function evalRpn (rpnQueue, map, item) {
  var outputStack = []
  nowmap = map
  nowItem = item
  rpnQueue = rpnQueue.map(d => d)

  while (rpnQueue.length > 0) {
    var cur = rpnQueue.shift()

    if (!isOperator(cur.subString)) {
      outputStack.push(cur)
    } else {
      if (outputStack.length < 2) {
        throw new Error('unvalid stack length')
      }
      var sec = outputStack.pop()
      var fir = outputStack.pop()

      outputStack.push(getResult(fir, sec, cur))
    }
  }

  if (outputStack.length !== 1) {
    throw new Error('unvalid expression')
  } else {
    if (!outputStack[0].flag) {
      return { value: getValue(outputStack[0]), state: STATUS.VALUE }
    }
    return outputStack[0]
  }
}

function getAllMatch (reg, str) {
  const result = []
  let myArray

  reg.lastIndex = 0
  while ((myArray = reg.exec(str + '')) !== null) {
    result.push(myArray[0])
  }
  return result
}

// 值类型推断
// KF的表，后端没有返回确定的数据格式，对时间等特殊格式只能采取类型推断。
export function toNumber (value) {
  value = value || ''
  if (!isNaN(value)) {
    return Number(value)
  }

  if (value === '--') return '--'
  value = '' + value
  if (value.charAt(value.length - 1) === 's') {
    value = value.substr(0, value.length - 1)
  }
  if (value.includes('%')) {
    value = toPoint(value)
    return value
  }
  // hh:mm:ss 格式的时间 换算规则
  if (/^\d+:[0-5]\d:[0-5]\d$/g.test(value)) {
    const [hh, mm, ss] = getAllMatch(/\d+/g, value)
    return Number(hh) * 3600 + Number(mm) * 60 + Number(ss)
  }

  return Number(getAllMatch(/\d+/g, value).join(''))
}

function toPoint (percent) {
  var str = percent.replace('%', '');
  str = (str / 100).toFixed(4);
  return Number(str);
}
function getResult (fir, sec, cur) {
  const v0 = getValue(fir)
  const v1 = getValue(sec)
  let value = 0

  switch (cur.subString) {
    case '+':
      value = v0 + v1
      break
    case '-':
      value = v0 - v1
      break
    case '*':
      value = v0 * v1
      break
    case '/':
      v1 === 0 ? value = 1 : value = v0 / v1
      break
    default:
      break
  }
  if (value < 0) {
    console.log(value, 123123123)
  }
  return {
    flag: true,
    state: STATUS.VALUE,
    value: value
  }
}

function getValue (token) {
  if (nowmap) {
    const factor = token.subString
    if (token.factor === '_const') {
      return Number(factor)
    } else if (token.state === STATUS.VALUE) {
      return Number(token.value)
    }
    return Number(toNumber(nowItem[nowmap[token.subString]]))
  }
  if (token.state === STATUS.VALUE) {
    return Number(token.value)
  } else {
    const factor = token.subString
    if (token.factor === '_const') {
      return Number(factor)
    }
    return toNumber(1)
  }
}

export function jisuan (data, formulas) {
  return new Promise((resolve, reject) => {
    const total = {}
    formulas.forEach(eq => (total[eq._id] = 0))
    data.forEach(item => {
      for (let i = 0; i < formulas.length; i++) {
        const e = formulas[i]

        try {
          const parseResult = parseFormulaStr(e.formulaStr)
          parseResult.forEach(s => {
            if (!isNaN(s.subString)) {
              s.factor = '_const'
            }
          })
          const rpns = dal2Rpn(parseResult)
          const result = evalRpn(rpns, e.variables, item)
          item[e._id] = getValue(result.value, e.digits, e.resultDisType)
          const reg = /\/[^0-9]/
          if (reg.test(e.formulaStr)) {
            if (item.flag === '总计') {
              total[e._id] = '--'
            }
          } else {
            if (item.flag !== '总计') {
              total[e._id] += result.value
            }
          }
        } catch (val) {
          console.log(val)
        }
      }
    })
    formulas.forEach(es => {
      if (!es.formulaStr.includes('/')) {
        total[es._id] = getValue(total[es._id], es.digits, es.resultDisType)
      }
    })
    for (let i = 0; i < data.length; i++) {
      const element = data[i];
      if (element.flag === '总计') {
        for (const key in total) {
          const val = total[key];
          data[i][key] = val
        }
      }
    }
    resolve(data)
  })
  function getValue (val, digits, resultDisType) {
    if (!isNaN(Number(val))) {
      if (resultDisType === 'default') {
        return Number(Number(val).toFixed(digits))
      } else {
        return (Number(val * 100).toFixed(digits)) + '%'
      }
    } else {
      return 0
    }
  }
}

export function format (time) {
  time = '' + time
  const flag = time.charAt(0) === '-'
  if (flag) {
    time = time.substr(1)
  }
  if (time.charAt(time.length - 1) === 's') {
    time = time.substr(0, time.length - 1)
    return Number(time)
  }
  const hour = time.split(':')[0];
  const min = time.split(':')[1];
  const sec = time.split(':')[2];
  const number = Number(hour * 3600) + Number(min * 60) + Number(sec)
  if (flag) {
    return -number
  }
  return number
}
